package com.skivingcloud.apiplatform.signature.util;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;

/**
 * SM国密加密算法实现
 */
public class SmCryptoUtils {

    /**
     * SM2加密
     * @param publicKey 公钥
     * @param data 数据
     * @return
     */
    public static String encrypt2Data(String publicKey, String data){
        //创建sm2 对象
        SM2 sm2 = getSM2(null, publicKey);
        String dataHex = sm2.encryptBcd(data, KeyType.PublicKey);
        return dataHex;
    }

    /**
     * 获取SM2加密工具对象
     *
     * @param privateKey 加密私钥
     * @param publicKey  加密公钥
     * @return 处理结果
     */
    private static SM2 getSM2(String privateKey, String publicKey) {
        ECPrivateKeyParameters ecPrivateKeyParameters = null;
        ECPublicKeyParameters ecPublicKeyParameters = null;
        if (StringUtils.isNotBlank(privateKey)) {
            ecPrivateKeyParameters = BCUtil.toSm2Params(privateKey);
        }
        if (StringUtils.isNotBlank(publicKey)) {
            if (publicKey.length() == 130) {
                //这里需要去掉开始第一个字节 第一个字节表示标记
                publicKey = publicKey.substring(2);
            }
            String xhex = publicKey.substring(0, 64);
            String yhex = publicKey.substring(64, 128);
            ecPublicKeyParameters = BCUtil.toSm2Params(xhex, yhex);
        }
        //创建sm2 对象
        SM2 sm2 = new SM2(ecPrivateKeyParameters, ecPublicKeyParameters);
        sm2.usePlainEncoding();
        sm2.setMode(SM2Engine.Mode.C1C3C2);
        return sm2;
    }

    /**
     * 生成一对 C1C3C2 格式的SM2密钥（sm2p256v1）
     *
     * @return 处理结果
     */
    public static Map<String, String> getSM2Key() {
        Map<String, String> map = new HashMap<>();
        //创建sm2 对象
        SM2 sm2 = SmUtil.sm2();
        sm2.setMode(SM2Engine.Mode.C1C3C2);
        byte[] privateKeyByte = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
        //这里公钥不压缩  公钥的第一个字节用于表示是否压缩  可以不要
        byte[] publicKeyByte = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
        try {
            String privateKey = HexUtil.encodeHexStr(privateKeyByte);
            String publicKey = HexUtil.encodeHexStr(publicKeyByte);
            map.put("publicKey", publicKey);
            map.put("privateKey", privateKey);
            return map;
        } catch (Exception e) {
//            log.error("获取SM2密钥出错", e);
            throw e;
        }
    }

    /**
     * SM2解密
     * @param dto 包含加解密相关参数信息的实体
     * @return 处理结果
     */
    /**
     * SM2解密
     * @param privateKey 私钥
     * @param dataHex 要解密的数据
     * @return
     */
    public static String decrypt2Data(String privateKey, String dataHex){
        try {
            //创建sm2 对象
            SM2 sm2 = getSM2(privateKey, null);
            String data = StrUtil.utf8Str(sm2.decryptFromBcd(dataHex, KeyType.PrivateKey));
            return data;
        } catch (Exception e) {
//            log.error("SM2解密失败", e);
            throw e;
        }
    }
//    read://https_blog.csdn.net/?url=https%3A%2F%2Fblog.csdn.net%2Fqq_37634156%2Farticle%2Fdetails%2F121606658

    /**
     * 获取一个随机的SM4密钥
     *
     * @return 处理结果
     */
    public static String getSM4Key() {
        return RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER, 16);
    }

    /**
     * 获取一个随机的AES密钥
     * @param length 长度16、32
     * @return
     */
    public static String getAesKey(int length){
        return RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER, length);
    }

    /**
     * 获取MD5随机salt
     * @return
     */
    public static String getMd5Salt(){
        return RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER, 32);
    }

    /**
     * 获取rsa秘钥对
     * @return
     */
    public static Map<String, String> getRsaKey() {
        Map<String, String> map = new HashMap<>();
        KeyPair keyPair = SecureUtil.generateKeyPair("RSA");
        String privateKey = HexUtil.encodeHexStr(keyPair.getPrivate().getEncoded());
        String publicKey = HexUtil.encodeHexStr(keyPair.getPublic().getEncoded());
        map.put("publicKey", publicKey);
        map.put("privateKey", privateKey);
        return map;
    }

    /**
     * 获取Hmac秘钥
     * @param algorithm
     * @return
     */
    public static String getHmacKey(String algorithm) {
        return HexUtil.encodeHexStr(SecureUtil.generateKey(algorithm).getEncoded());
    }

    /**
     * SM4加密
     *
     * @param key 秘钥
     * @param data 数据
     * @return 处理结果
     */
    public static String encrypt4Data(String key, String data) {
        try {
            SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes(CharsetUtil.CHARSET_UTF_8));
            return sm4.encryptHex(data);
        } catch (Exception e) {
           throw e;
        }
    }

    /**
     * SM4解密
     * @param key 秘钥
     * @param dataHex 待解密数据
     * @return
     */
    public static String decrypt4Data(String key, String dataHex) {
        try {
            SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes(CharsetUtil.CHARSET_UTF_8));
            String data = sm4.decryptStr(dataHex);
            return data;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * sm3加密（摘要算法）
     * @param key salt
     * @param data 待加密数据
     * @return
     */
    public static String encrypt3Data(String key, String data){
        return SmUtil.sm3WithSalt(key.getBytes(StandardCharsets.UTF_8)).digestHex(data);
    }

    public static void main(String[] args) {
        String publicKeyHex = null;
        String privateKeyHex = null;
        Map<String, String> keyMap = getSM2Key();
        publicKeyHex = "04a836b39e396797ff6fe22f207bd941a161ef714593a7acea1916e392420eff5e00a2af4ede67d19844e2b90a03b596c37e6c3e784ce1e294e9636dec285db81b";
        privateKeyHex = "009f6287764561f7716244d2910cabbefe89e771f64dde17b39af83e1332bcd422";
        System.out.println("---->SM2公钥：" + publicKeyHex);
        System.out.println("---->SM2私钥：" + privateKeyHex);

        /**
         * 公钥加密
         */
        String data = "=========待加密数据=========";

        String hutoencryptData = encrypt2Data(publicKeyHex, data);
        System.out.println("-->hu加密结果：" + hutoencryptData);
        /**
         * 私钥解密
         */
        //将十六进制私钥串转换为 BCECPrivateKey 私钥对象
        String hutdecryptData = decrypt2Data(privateKeyHex, hutoencryptData);
        System.out.println("-->hu解密结果：" + hutdecryptData);
        System.out.println("-->hu验签结果：" + sm2Verify(hutoencryptData, data, privateKeyHex));
    }

    /**
     * sm2使用私钥对数据进行验签
     * @param signatureParam
     * @param map
     * @param privateKey
     */
    public static boolean sm2Verify(String signatureParam, SortedMap<String, Object> map, String privateKey) {
        SM2 sm2 = getSM2(privateKey, null);
        String data = sm2.decryptStr(signatureParam, KeyType.PrivateKey);
        if (data.equals(JSONUtil.toJsonStr(map))) {
            return true;
        } else{
            return false;
        }
//         return sm2.verify(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8), signatureParam.getBytes(StandardCharsets.UTF_8));
    }

    public static boolean sm2Verify(String signatureParam, String param, String privateKey) {
        SM2 sm2 = getSM2(privateKey, null);
        String data = sm2.decryptStr(signatureParam, KeyType.PrivateKey);
        if (data.equals(param)) {
            return true;
        } else{
            return false;
        }
//        return sm2.verify(param.getBytes(StandardCharsets.UTF_8), signatureParam.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * sm3使用秘钥进行验签
     * @param signatureParam
     * @param sortedMap
     * @param appSecret
     * @return
     */
    public static boolean sm3Verify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        String s1 = SmUtil.sm3WithSalt(appSecret.getBytes(StandardCharsets.UTF_8)).digestHex(JSONUtil.toJsonStr(sortedMap));
        if (signatureParam.equals(s1)) {
            return true;
        } else{
            return false;
        }
    }

    /**
     * sm4使用秘钥进行验签
     * @param signatureParam
     * @param sortedMap
     * @param appSecret
     * @return
     */
    public static boolean sm4Verify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        SymmetricCrypto sm4 = SmUtil.sm4(appSecret.getBytes(CharsetUtil.CHARSET_UTF_8));
        String data = sm4.encryptHex(JSONUtil.toJsonStr(sortedMap));
        if(data.equals(signatureParam)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * aes使用秘钥进行验签 因受美国政策影响，aes256进行加密时，部分同学可能会出现无法加密进而报异常 ：
     * @param signatureParam
     * @param sortedMap
     * @param appSecret
     * @return
     */
    public static boolean aesVerify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        AES aes = SecureUtil.aes(appSecret.getBytes(StandardCharsets.UTF_8));
        String data = aes.encryptHex(JSONUtil.toJsonStr(sortedMap));
        if(data.equals(signatureParam)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * md5使用秘钥进行验签
     * @param signatureParam
     * @param sortedMap
     * @param appSecret
     * @return
     */
    public static boolean md5Verify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        String data = SecureUtil.md5(appSecret + JSONUtil.toJsonStr(sortedMap));
        if(data.equals(signatureParam)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * sha256验签
     * @param signatureParam
     * @param sortedMap
     * @param appSecret
     * @return
     */
    public static boolean sha256Verify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        String data = SecureUtil.sha256(appSecret + JSONUtil.toJsonStr(sortedMap));
        if(data.equals(signatureParam)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * sha512验签
     * @param signatureParam 加密数据
     * @param sortedMap 明文
     * @param appSecret 秘钥
     * @return
     */
    public static boolean sha512Verify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        String data = DigestUtil.sha512Hex(appSecret + JSONUtil.toJsonStr(sortedMap));
        if(data.equals(signatureParam)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * rsa验签
     * @param signatureParam 加密数据
     * @param sortedMap 明文
     * @param appSecret 秘钥
     * @return
     */
    public static boolean rsaVerify(String signatureParam, SortedMap<String, Object> sortedMap, String appSecret) {
        String data = SecureUtil.rsa(appSecret, null).decryptStr(signatureParam, KeyType.PrivateKey);
        if (data.equals(JSONUtil.toJsonStr(sortedMap))) {
            return true;
        } else{
            return false;
        }
    }

    /**
     * hmac验签
     * @param algorithm 加密方式HmacMD5("HmacMD5"),
     *      HmacSHA1("HmacSHA1"),
     *      HmacSHA256("HmacSHA256"),
     *      HmacSHA384("HmacSHA384"),
     *      HmacSHA512("HmacSHA512"),
     *      HmacSM3("HmacSM3"),
     *      SM4CMAC("SM4CMAC")
     * @param signatureParam 密文
     * @param sortedMap 明文
     * @param appSecret 秘钥
     * @return
     */
    public static boolean hmacVerify(String algorithm,String signatureParam, SortedMap<String, Object> sortedMap, String appSecret){
        String data = "";
        switch(algorithm){
            case "HmacMD5":
                data = SecureUtil.hmac(HmacAlgorithm.HmacMD5, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            case "HmacSHA1":
                data = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            case "HmacSHA256":
                data = SecureUtil.hmac(HmacAlgorithm.HmacSHA256, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            case "HmacSHA384":
                data = SecureUtil.hmac(HmacAlgorithm.HmacSHA384, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            case "HmacSHA512":
                data = SecureUtil.hmac(HmacAlgorithm.HmacSHA512, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            case "HmacSM3":
                data = SecureUtil.hmac(HmacAlgorithm.HmacSM3, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            case "SM4CMAC":
                data = SecureUtil.hmac(HmacAlgorithm.SM4CMAC, appSecret).digestHex(JSONUtil.toJsonStr(sortedMap));
                break;
            default:
                break;
        }
        if (data.equals(signatureParam)) {
            return true;
        } else{
            return false;
        }
    }
    /**
     * AES128加密
     * @param key
     * @param toJsonStr
     * @return
     */
    public static String encryptAES128(String key, String toJsonStr) {
        AES aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));
        return aes.encryptHex(toJsonStr);
    }

    /**
     * 不要用这个
     * AES256加密 因受美国政策影响，aes256进行加密时，部分同学可能会出现无法加密进而报异常 ：
     * @param key
     * @param toJsonStr
     * @return
     */
    @Deprecated
    public static String encryptAES256(String key, String toJsonStr) {
//        因受美国政策影响，aes256进行加密时，部分同学可能会出现无法加密进而报异常 ：
//        java.security.InvalidKeyException: Illegal key size or default parameters
        AES aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));
        return aes.encryptHex(toJsonStr);
    }

    /**
     * MD5加密
     * @param key
     * @param toJsonStr
     * @return
     */
    public static String encryptMD5(String key, String toJsonStr) {
        return SecureUtil.md5(key + toJsonStr);
    }

    /**
     * SHA256加密
     * @param key
     * @param toJsonStr
     * @return
     */
    public static String encryptSHA256(String key, String toJsonStr) {
        return DigestUtil.sha256Hex(key + toJsonStr);
    }

    /**
     * SHA512加密
     * @param key
     * @param toJsonStr
     * @return
     */
    public static String encryptSHA512(String key, String toJsonStr) {
        return DigestUtil.sha512Hex(key + toJsonStr);
    }

    /**
     * RSA加密
     * @param key
     * @param toJsonStr
     * @return
     */
    public static String encryptRsa(String key, String toJsonStr) {
        RSA rsa = SecureUtil.rsa(null, key);
        return rsa.encryptHex(toJsonStr, KeyType.PublicKey);
    }

    /**
     * HMAC加密
     * @param algorithm HmacMD5("HmacMD5"),
     * HmacSHA1("HmacSHA1"),
     * HmacSHA256("HmacSHA256"),
     * HmacSHA384("HmacSHA384"),
     * HmacSHA512("HmacSHA512"),
     * HmacSM3("HmacSM3"),
     * SM4CMAC("SM4CMAC")
     * @param key
     * @param toJsonStr
     * @return
     */
    public static String encryptHmac(HmacAlgorithm algorithm, String key, String toJsonStr){
        return SecureUtil.hmac(algorithm, key).digestHex(toJsonStr);
    }

}
